package lhxia.translater.http.server;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;
import java.nio.channels.SocketChannel;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import lhxia.translater.http.server.ThreadPool.WokerThread.Attach;

public class ThreadPool {
	public static int MAX_CONNECT = 3;
	ThreadPoolExecutor pool;
	Hashtable<String, WokerThread> canUse = new Hashtable<String, WokerThread>();
	
	private static ThreadPool instance = new ThreadPool();
	
	public ThreadPool() {
		init();
	}
	
	public static ThreadPool getInstance(){
		return instance;
	}
	
	private void init(){
		pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());
		
	}
	
	public Object[] submit(SocketChannel connect, Pipe recevQueue){
		synchronized (canUse) {
			Object[] r = new Object[2];
			WokerThread thread = null;
			Attach att = null;
			Collection<WokerThread> canuses = canUse.values();
			Iterator<WokerThread> it = canuses.iterator();
			if(it.hasNext()){
				thread = it.next();
				att = thread.attachConnect(connect, recevQueue);
				System.out.println("old");
			}else {
				thread = new WokerThread();
				att = thread.attachConnect(connect, recevQueue);
				pool.submit(thread);
				System.out.println("new ");
			}
			r[0] = thread;
			r[1] = att;
			return r;
		}
	}
	
	public class WokerThread implements Runnable{
		
		private String key;
		private boolean isNeedSend = false;
		ByteBuffer buffer;
		
		int connectCount = 0;
		
		private Vector<Attach> attchList;
		
		private boolean isRunning = true;
		
		public WokerThread() {
			key = UUID.randomUUID().toString();
			attchList = new Vector<WokerThread.Attach>();
			buffer = ByteBuffer.allocate(4096);
		}
		
		public Attach attachConnect(SocketChannel connect, Pipe recevQueue){
			synchronized (attchList) {
				Attach a = new Attach(connect, recevQueue, new DataHandler());
				a.handler.onConnect(connect);
				attchList.add(a);
				connectCount++;
				if(connectCount == MAX_CONNECT){
					canUse.remove(key);
					System.out.println("remove1");
				}else {
					canUse.put(key, this);
					System.out.println("put1");
				}
				return a;
			}
		}
		
		public void disattachConnect(Attach connect){
			synchronized (attchList) {
				connect.isDirt = true;
				connect.handler.onDisConnect(connect.connect);
				attchList.remove(connect);
				
				connectCount--;
				if(connectCount <= 0){
					canUse.remove(key);
					System.out.println("remove");
				}else {
					canUse.put(key, this);
					System.out.println("put");
				}
//				isRunning = false;
//				notify();
			}
		}
		
		public void put(Pipe pip, ByteBuffer buf){
			try {
				pip.sink().write(buf);
			} catch (IOException e) {
				e.printStackTrace();
			}
			synchronized (this) {
				isNeedSend = true;
				notify();
			}
		}

		@Override
		public void run() {
			Iterator<Attach> ait = null;
			Attach att = null;
			while(isRunning){
				isNeedSend = false;
				ait = attchList.iterator();
				while(ait.hasNext()){
					att = ait.next();
					if(att.isDirt){
						ait.remove();
						continue;
					}
					int num = 1;
					int count = 10;
					while(count-- > 0 && num != 0){
						buffer.clear();
						try {
							num = att.pip.source().read(buffer);
							if(num == 0){
								break;
							}
						} catch (IOException e) {
							e.printStackTrace();
						}
						buffer.flip();
						att.handler.handleData(att.connect, buffer, num);
					}
				}
				try {
					synchronized (this) {
						if(!isRunning){
							break;
						}
						if(isNeedSend){
							continue;
						}else {
							isNeedSend = false;
						}
						wait(60000);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
		public class Attach {
			boolean isDirt;
			SocketChannel connect;
			private Pipe pip;
			private DataHandler handler;
			
			public Attach(SocketChannel connect,
					Pipe pip, DataHandler handler) {
				super();
				this.connect = connect;
				this.pip = pip;
				this.handler = handler;
			}
			
			@Override
			public boolean equals(Object o) {
				Attach oo = (Attach) o;
				return connect.equals(oo.connect);
			}
			
			@Override
			public int hashCode() {
				return connect.hashCode();
			}

			public Pipe getPip() {
				return pip;
			}

			public void setPip(Pipe pip) {
				this.pip = pip;
			}

			public DataHandler getHandler() {
				return handler;
			}

			public void setHandler(DataHandler handler) {
				this.handler = handler;
			}
		}

		public String getKey() {
			return key;
		}

		public void setKey(String key) {
			this.key = key;
		}
	}
}